package luz.javaproxy.plugins.ProxySelector;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.LayoutStyle;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;

import luz.javaproxy.common.Tuple;
import luz.javaproxy.plugin.PluginInterface;
import luz.javaproxy.plugins.ProxySelector.ProxySelectorTable.ActiveProxyListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;


public class ProxySelector extends JPanel implements PluginInterface {
	private static final long serialVersionUID = -2276445160555531302L;
	private static final Log log = LogFactory.getLog(ProxySelector.class);
	private JButton btnRandom;
	private JButton bntNext;
	private JButton btnPrevious;
	private JButton btnImport;
	private JScrollPane scrProxy;
	private JButton btnDelete;
	private JButton bntAdd;
	private ProxySelectorTable tblProxy;
	List<Tuple<String,Boolean>> proxies;
	private Tuple<String,Boolean> activeProxy;
	private Pattern proxyPattern = Pattern.compile("((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]):(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,4})");
	private JFileChooser fileChooser;
	
	
	public static void main(String[] args) {
		JFrame frame = new JFrame();
		frame.getContentPane().add(new ProxySelector());
		frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		frame.pack();
		frame.setVisible(true);
	}
	

	
	public ProxySelector(){
		super();		
		fileChooser = new JFileChooser();
		initGUI();
	}

	
	private void initGUI() {
		try {
			GroupLayout thisLayout = new GroupLayout((JComponent)this);
			this.setLayout(thisLayout);
			{
				scrProxy = new JScrollPane();
				{
					tblProxy = new ProxySelectorTable();
					tblProxy.setActiveProxyListener(new ActiveProxyListener(){
						public void proxyChanged(int row, boolean enabled) {
							selectProxy(row, enabled);				
						}
					});
					scrProxy.setViewportView(tblProxy);
				}
			}
			{
				btnImport = new JButton();
				btnImport.setText("Import");
				btnImport.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						importProxies();
					}
				});
			}
			{
				bntAdd = new JButton();
				bntAdd.setText("Add");
				bntAdd.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						addProxy();
					}
				});
			}
			{
				btnDelete = new JButton();
				btnDelete.setText("Delete");
				btnDelete.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						removeSelectedProxies();
					}
				});
			}
			{
				btnPrevious = new JButton();
				btnPrevious.setText("Previous");
				btnPrevious.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						selectProxyDiff(-1);
					}
				});
			}
			{
				bntNext = new JButton();
				bntNext.setText("Next");
				bntNext.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						selectProxyDiff(+1);
					}
				});
			}
			{
				btnRandom = new JButton();
				btnRandom.setText("Random");
				btnRandom.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						selectProxyDiff(new Random().nextInt(Integer.MAX_VALUE));
					}
				});
			}
			thisLayout.setVerticalGroup(thisLayout.createSequentialGroup()
				.addContainerGap()
				.addGroup(thisLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
				    .addComponent(btnImport, GroupLayout.Alignment.BASELINE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
				    .addComponent(bntAdd, GroupLayout.Alignment.BASELINE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
				    .addComponent(btnDelete, GroupLayout.Alignment.BASELINE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE))
				.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
				.addGroup(thisLayout.createParallelGroup()
				    .addGroup(GroupLayout.Alignment.LEADING, thisLayout.createSequentialGroup()
				        .addComponent(btnPrevious, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
				        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
				        .addComponent(bntNext, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
				        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
				        .addComponent(btnRandom, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE))
				    .addComponent(scrProxy, GroupLayout.Alignment.LEADING, 0, 92, Short.MAX_VALUE)));
			thisLayout.setHorizontalGroup(thisLayout.createSequentialGroup()
				.addGroup(thisLayout.createParallelGroup()
					.addGroup(thisLayout.createSequentialGroup()
						.addComponent(scrProxy, 0, 253, Short.MAX_VALUE)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addGroup(thisLayout.createParallelGroup()
						    .addComponent(btnPrevious, GroupLayout.Alignment.LEADING, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
						    .addComponent(bntNext, GroupLayout.Alignment.LEADING, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)
						    .addComponent(btnRandom, GroupLayout.Alignment.LEADING, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)))
					.addGroup(GroupLayout.Alignment.LEADING, thisLayout.createSequentialGroup()
				        .addPreferredGap(scrProxy, btnImport, LayoutStyle.ComponentPlacement.INDENT)
				        .addComponent(btnImport, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)
				        .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
				        .addComponent(bntAdd, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)
				        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
				        .addComponent(btnDelete, GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)))
				.addContainerGap());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	

	
	@Override
	public JPanel getPluginPanel() {
		return this;
	}

	@Override
	public Properties getSettings() {
		Properties conf = new Properties();
		for (Tuple<String,Boolean> tuple: proxies)
			conf.setProperty(tuple.getFirst(), tuple.getSecond().toString());
		return conf;
	}

	@Override
	public void setSettings(Properties config) {
		proxies = new LinkedList<Tuple<String,Boolean>>();
		boolean enabled;
		boolean foundActive=false;
		String proxy;
		Tuple<String, Boolean> tuple;
		for (Entry<Object, Object> entry : config.entrySet()) {
			proxy=(String)entry.getKey();
			if (foundActive){
				tuple=new Tuple<String, Boolean>(proxy, false);
			}else{
				enabled=Boolean.valueOf((String)entry.getValue());
				tuple=new Tuple<String, Boolean>(proxy, enabled);
				if (enabled){
					activeProxy=tuple;
					foundActive=true;
				}
			}
			proxies.add(tuple);
		}
		tblProxy.setProxies(proxies);
	}

	@Override
	public void onRequest(HttpRequest request) {
		if (activeProxy!=null && activeProxy.getSecond()){
			String address=activeProxy.getFirst();
			String host=request.getFirstHeader("host").getValue();
			if (address!=null){
				log.info("changing from: "+host+" to: "+address);
				HttpParams params  = new BasicHttpParams();
				params.setParameter(PluginInterface.PROXY, address);
				request.setParams(params);
			}
		}
	}

	@Override
	public void onResponse(final HttpRequest request, final HttpResponse response) {
		//Nothing
	}
	
	
	private void importProxies() {
		int response = fileChooser.showDialog(this, null);
		if (response==JFileChooser.APPROVE_OPTION){
			File file = fileChooser.getSelectedFile();
			try {
				FileInputStream fis = new FileInputStream(file);
				Reader fr = new InputStreamReader(fis, Charset.defaultCharset());
				BufferedReader br = new BufferedReader(fr);
				String line;
				StringBuilder sb = new StringBuilder();
				while ((line = br.readLine()) != null)
					sb.append(line);
				br.close();	
				
				String input = sb.toString();
				Matcher m = proxyPattern.matcher(input);
				while (m.find())
					proxies.add(new Tuple<String, Boolean>(m.group(), false));
				tblProxy.updateTable();
			} catch (FileNotFoundException e) {
				log.warn("File not found "+file.getAbsolutePath());
			} catch (IOException e) {
				log.warn("Error reading file "+file.getAbsolutePath(), e);
			}
		}
	}
	
	private void addProxy() {	
		String proxy = JOptionPane.showInputDialog("Please input a proxy: (xxx.xxx.xxx.xxx:xxx)"); 
		if (proxy!=null && proxyPattern.matcher(proxy).matches()){
			proxies.add(new Tuple<String, Boolean>(proxy, false));
			((AbstractTableModel)tblProxy.getModel()).fireTableRowsInserted(proxies.size(), proxies.size());
		}
	}

	private void removeSelectedProxies(){
		int[] selectedRows = tblProxy.getSelectedRows();
		for (int i = selectedRows.length-1; i>=0; i--)
			proxies.remove(selectedRows[i]);
		tblProxy.updateTable();
	}

	private void selectProxyDiff(int diff) {
		int size = proxies.size();
		int row = proxies.indexOf(activeProxy);
		row=(row+size+diff) % size;	
		selectProxy(row, true);
	}
	
	private void selectProxy(int row, boolean enabled) {
		if (enabled){
			if (activeProxy!=null)
				activeProxy.setSecond(false);
			activeProxy=proxies.get(row);
		}
		activeProxy.setSecond(enabled);
		tblProxy.updateTable();
		tblProxy.scrollRectToVisible(tblProxy.getCellRect(row, 0, true));
	}



}
